package cn.pku.eecs.ldn.view;

import java.util.Observable;
import java.util.Observer;

import cn.pku.eecs.ldn.view.helper.ZoomOnTouchListener;
import cn.pku.eecs.ldn.view.helper.ZoomState;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.util.Log;
import android.view.GestureDetector;
import android.view.View;

/**
 * A view that displays and zooms an image.
 * @author ldn
 */
public class ZoomImageView extends View implements Observer{
	private final static String TAG = "ImageViewZoomInAndOut";
	private final static Boolean DEBUG = true;
	
	//I don't know why FILTER_BITMAP_FLAG should be used instead of others
	private Paint mPaint = new Paint(Paint.FILTER_BITMAP_FLAG); 
    private Rect mRectSrc = new Rect();
    private Rect mRectDst = new Rect();
   
    private int viewWidth = 0;
    private int viewHeight = 0;
    private int bitmapWidth = 0;
    private int bitmapHeight = 0;
    private int centerX = 0, centerY = 0;
    
    private float zoomTemp;
    
    private Bitmap loadingBitmap = null;
	private Bitmap mBitmap = null;
    private ZoomState mZoomState = null;
    protected ZoomOnTouchListener zoomOnTouchListener = null;
    
    private boolean isInitialized = false;
    private boolean isReadyToInit = false;
    
    private Matrix mMatrix = null;
    private RectF rectFLoadingBitmap = null;
    private RectF rectFCanvas = null;
    
    private final int MSG_LOADING = 0;
    private final int MSG_SET_BITMAP = 1;
    Handler handler = new Handler() {
    	@Override
		public void handleMessage(Message msg) {
    		if(msg.what == MSG_LOADING) {
    			invalidate();
    		}
    		else if(msg.what == MSG_SET_BITMAP){
    			if(DEBUG) Log.d(TAG, "handleMessage(): set bitmap");
        		mZoomState.init(bitmapWidth, bitmapHeight, viewWidth, viewHeight, centerX, centerY);
            	isInitialized = true;
            	if(loadingBitmap != null && loadingBitmap.isRecycled() == false) {
            		loadingBitmap.recycle();
            	}
            	invalidate();
    		}
    	}
    };
    
    public ZoomImageView(Context context) {
    	super(context);
    	init();
    }
    
    public ZoomImageView(Context context, AttributeSet attrs) {
		super(context, attrs);
		init();
	}

    protected void init() {
		Log.d(TAG, "init(): init in constructor");
		mZoomState = new ZoomState();
		mZoomState.addObserver(this);
		zoomOnTouchListener = new ZoomOnTouchListener(mZoomState, getContext());
		setOnTouchListener(zoomOnTouchListener);
	}

    /**
     * 设置加载时显示的bitmap。加载完成后，如调用setBitmap()有效后，此bitmap将被销毁以节省内存。
     * @param bitmap
     */
    public void setLoadingBitmap(Bitmap bitmap) {
    	if(DEBUG) Log.d(TAG, "setLoadingBitmap()");
    	if(bitmap == null) {
    		if(DEBUG) Log.e(TAG, "setLoadingBitmap(): bitmap is null");
    		return;
    	}
    	loadingBitmap = bitmap;
    	mMatrix = new Matrix();
    	rectFLoadingBitmap = new RectF(0, 0, loadingBitmap.getWidth(), loadingBitmap.getHeight());
    	rectFCanvas = new RectF();
    	handler.sendEmptyMessage(MSG_LOADING);
    }
    
    /**
     * 设置要显示的图片
     * @param bitmap
     */
    public void setBitmap(Bitmap bitmap) {
    	if(DEBUG) Log.d(TAG, "setBitmap()");
    	if(bitmap == null) {
    		if(DEBUG) Log.e(TAG, "setBitmap(): bitmap is null");
    		return;
        }
        mBitmap = bitmap;
        bitmapWidth = mBitmap.getWidth();
        bitmapHeight = mBitmap.getHeight();
        if(isReadyToInit == true) {
        	handler.sendEmptyMessage(MSG_SET_BITMAP);
        }
        else {
        	invalidate();
        }
    }

	public void setOnDoubleTapListener(GestureDetector.OnDoubleTapListener onDoubleTapListener) {
		if(zoomOnTouchListener == null) {
			if(DEBUG) Log.e(TAG, "setOnDoubleTapListener(): zoomOnTouchListener == null");
			return;
		}
		zoomOnTouchListener.setOnDoubleTapListener(onDoubleTapListener);
	}
	
	public ZoomState getZoomState() {
    	return mZoomState;
    }
	
	@Override
	protected void onDraw(Canvas canvas) {
		if(mBitmap == null) {
			if(loadingBitmap != null) {
				//display the loadingBitmap from setLoadingBitmap()
				rectFCanvas.left = getLeft();
				rectFCanvas.right = getRight();
				rectFCanvas.top = getTop();
				rectFCanvas.bottom = getBottom();
				if(mMatrix.setRectToRect(rectFLoadingBitmap, rectFCanvas, Matrix.ScaleToFit.CENTER)) {
					canvas.drawBitmap(loadingBitmap, mMatrix, mPaint);
				}
				else {
					if(DEBUG) Log.e(TAG, "onDraw(): falied mapping rect");
				}
			}
			return;
		}
		if(isInitialized == false) {
			return;
		}
		
		zoomTemp = mZoomState.getZoom();
    	mRectSrc = mZoomState.getBitmapRect();
    	mRectDst = mZoomState.getViewRect();
    	
    	//避免对超过bitmap内容的区域绘图，因此需要将srcRect缩小，相应的dstRect也要缩小
		if (mRectSrc.left < 0) {
			mRectDst.left += -mRectSrc.left * zoomTemp;
			mRectSrc.left = 0;
		}
		if (mRectSrc.right > bitmapWidth) {
			mRectDst.right -= (mRectSrc.right - bitmapWidth) * zoomTemp;
			mRectSrc.right = bitmapWidth;
		}
	    if (mRectSrc.top < 0) {
	        mRectDst.top += -mRectSrc.top * zoomTemp;
	        mRectSrc.top = 0;
	    }
	    if (mRectSrc.bottom > bitmapHeight) {
	    	mRectDst.bottom -= (mRectSrc.bottom - bitmapHeight) * zoomTemp;
	    	mRectSrc.bottom = bitmapHeight;
	    }

	    canvas.rotate(mZoomState.getRotateDegree(), centerX, centerY);
        canvas.drawBitmap(mBitmap, mRectSrc, mRectDst, mPaint);
    }
	
	@Override
	public void update(Observable arg0, Object data) {
		if(data == null || data.equals(ZoomState.FLAG_UI_THREAD) == true) {
			invalidate();
		}
		else if(data.equals(ZoomState.FLAG_NONE_UI_THREAD) == true){
			postInvalidate();
		}
		else {
			if(DEBUG) Log.e(TAG, "update(): unknown thread called update, refuse");
		}
	}
	
	@Override
	protected void onLayout(boolean changed, int left, int top, int right,
			int bottom) {
		super.onLayout(changed, left, top, right, bottom);
		viewWidth = right - left;
		viewHeight = bottom - top;
		centerX = (getRight()-getLeft()) / 2;
    	centerY = (getBottom()-getTop()) / 2;
		if(DEBUG) Log.d(TAG, "onLayout(): " + viewWidth + " " + viewHeight);
    	isReadyToInit = true;
    	
		//we must get viewWidth and viewHeight before setting bitmap
		if(isInitialized == false && mBitmap != null) {
			handler.sendEmptyMessage(MSG_SET_BITMAP);
		}
	}
}
